package app

import (
	"fmt"
	"os"
	"path/filepath"
	"reflect"
	"strconv"
	"strings"
	"sync"
	"time"

	"gitee.com/tomatomeatman/golang-repository/bricks2/function/data/aesutil"
	"gitee.com/tomatomeatman/golang-repository/bricks2/model/msgentity"
	Log "github.com/cihub/seelog"
	"github.com/fsnotify/fsnotify"
	"gopkg.in/ini.v1"
)

var (
	glbAppiCloudApp    = 0                                //是否要加入分布式系统(0:待初始化;1:是;2:否)
	glbAppiCloudSystem = 0                                // 判断程序是否启用桥接
	configData         = map[string]map[string]*ini.Key{} //配置文件配置项
	watchOnce          sync.Once                          //监听文件变化单例
)

// type AppUtil struct{}

// 判断程序是否要加入分布式系统
func IsCloudApp() bool {
	if glbAppiCloudApp != 0 {
		return glbAppiCloudApp == 1
	}

	if HasSection("CloudCenter") {
		glbAppiCloudApp = 1
		return true
	}

	glbAppiCloudApp = 2
	return false
}

// 判断程序是否非分布式系统
func IsNotCloudApp() bool {
	return !IsCloudApp()
}

// 判断程序是否启用桥接
func IsCloudSystem() bool {
	if glbAppiCloudSystem != 0 {
		return glbAppiCloudSystem == 1
	}

	if HasSection("CloudSystem") {
		glbAppiCloudSystem = 1
		return true
	}

	glbAppiCloudSystem = 2
	return false
}

// 判断程序是否禁用桥接
func IsNotCloudSystem() bool {
	return !IsCloudSystem()
}

// 判断配置组是否存在
func HasSection(sectionName string) bool {
	iniCfg()

	section, ok := configData[sectionName]
	if !ok || section == nil || len(section) == 0 {
		return false
	}

	return len(section) > 0
}

// 读取配置值
func ReadConfigKey(section, key string, def interface{}) interface{} {
	iniCfg()

	keys, ok := configData[section]
	if !ok || keys == nil {
		return def
	}

	value, ok := keys[key]
	if !ok {
		return def
	}

	if value == nil {
		return def
	}

	if def == nil {
		def = ""
	}

	isEncrypt := false
	temp := fmt.Sprintf("%v", value)
	if strings.HasPrefix(temp, "${") && strings.HasSuffix(temp, "}") {
		isEncrypt = true
		temp = aesutil.Decrypt(temp[2:len(temp)-1], "bricks")
	}

	switch def.(type) {
	case string: // 将interface转为string字符串类型
		if isEncrypt {
			return temp
		}
		return value.String()
	case int:
		if isEncrypt {
			result, err := strconv.Atoi(temp)
			if err != nil {
				return def
			}
			return result
		}

		result, err := value.Int()
		if err != nil {
			return def
		}

		return result
	case int64:
		if isEncrypt {
			result, err := strconv.ParseInt(temp, 10, 64)
			if err != nil {
				return def
			}
			return result
		}

		result, err := value.Int64()
		if err != nil {
			return def
		}

		return result
	case float64:
		if isEncrypt {
			result, err := strconv.ParseFloat(temp, 64)
			if err != nil {
				return def
			}
			return result
		}

		result, err := value.Float64()
		if err != nil {
			return def
		}

		return result
	case bool:
		if isEncrypt {
			result, err := strconv.ParseBool(temp) //接受 1, t, T, TRUE, true, True, 0, f, F, FALSE, false, False 等字符串；
			if err != nil {
				return def
			}
			return result
		}

		result, err := value.Bool()
		if err != nil {
			return def
		}

		return result
	default:
		return value.String()
	}
}

// 初始化配置文件变量
func iniCfg() {
	if len(configData) > 0 {
		return //已经读取
	}

	root := ""
	exePath, err := os.Executable()
	if err != nil {
		root = "."
	}

	root, _ = filepath.EvalSymlinks(filepath.Dir(exePath))

	configFilePath := strings.Replace(root+"/config/app.ini", "\\", "/", -1)

	_, err = os.Stat(configFilePath) //os.Stat获取文件信息
	if err != nil {
		if !os.IsExist(err) {
			Log.Error("配置文件不存在", err)
			return
		}
	}

	appCfg, err := ini.Load(configFilePath)
	if err != nil {
		Log.Error("配置文件读取错误", err)
		return
	}

	sections := appCfg.Sections()
	for _, section := range sections {
		keys := make(map[string]*ini.Key)
		for _, key := range section.Keys() {
			keys[key.Name()] = key
		}

		configData[section.Name()] = keys
	}

	watchOnce.Do(func() {
		go watchConfigFile(configFilePath) //监听文件变化
	})
}

/**
 * 通过结构体获取
 * @param entity 结构体样式
 * @param names 辅助'项名称',若不传递则按结构体名称做'项名称'
 * @return 返回新建结构体实体
 */
func ToEntity(entity interface{}, names ...string) *msgentity.MsgEntity {
	if entity == nil {
		entity = map[string]interface{}{}
	}

	iniCfg()

	rt := reflect.TypeOf(entity)
	rve := reflect.New(rt).Elem()

	if len(names) < 1 {
		Name := rt.Name()
		if strings.Contains(Name, "map[string]") || strings.HasPrefix(Name, "[]") {
			return msgentity.Err(1002, "不明确的配置项,无法进行解析")
		}

		names = append(names, Name)
	}

	for _, Name := range names {
		section, ok := configData[Name]
		if !ok || section == nil {
			continue
		}

		for k := 0; k < rt.NumField(); k++ {
			value, ok := section[rt.Field(k).Name]
			if !ok || value == nil {
				continue
			}

			field := rve.FieldByName(rt.Field(k).Name)
			if !field.IsValid() {
				continue
			}

			{
				temp := value.String()
				if strings.Contains(temp, "${") {
					continue //配置中含有变量符,不获取(待改进)
				}
			}

			switch field.Type().String() {
			case "string":
				field.Set(reflect.ValueOf(value.String()))
			case "int":
				temp, err := value.Int()
				if err != nil {
					continue
				}

				field.Set(reflect.ValueOf(temp))
			case "int64":
				temp, err := value.Int64()
				if err != nil {
					continue
				}

				field.Set(reflect.ValueOf(temp))
			case "bool":
				temp, err := value.Bool()
				if err != nil {
					continue
				}

				field.Set(reflect.ValueOf(temp))
			case "float64":
				temp, err := value.Float64()
				if err != nil {
					continue
				}

				field.Set(reflect.ValueOf(temp))
			default:
				field.Set(reflect.ValueOf(value.String()))
			}
		}
	}

	result := rve.Interface()

	return msgentity.Success(result, "转换结束")
}

/**
 * 释放配置文件
 */
func FreeappCfg() {
	configData = make(map[string]map[string]*ini.Key)
}

// 使用 fsnotify 监听配置文件的变化,注意调用watchConfigFile时必须使用go watchConfigFile(),否则会造成阻塞
func watchConfigFile(filePath string) {
	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		Log.Error("开启配置文件监听失败", err)
	}
	defer watcher.Close()

	if err := watcher.Add(filePath); err != nil {
		Log.Error("添加配置文件到监听器发生异常:", err)
	}

	var lastModTime time.Time
	fileInfo, err := os.Stat(filePath)
	if err == nil {
		lastModTime = fileInfo.ModTime()
	}

	// done := make(chan bool)

	func() {
		for {
			select {
			case event, ok := <-watcher.Events:
				if !ok {
					return
				}

				if event.Op&fsnotify.Write == fsnotify.Write {
					// 检查文件的最后修改时间是否发生变化
					fileInfo, err := os.Stat(filePath)
					if err != nil {
						Log.Error("获取文件修改时间失败:", err)
						continue
					}

					if fileInfo.ModTime().After(lastModTime) {
						lastModTime = fileInfo.ModTime()
						Log.Error("配置文件被修改,即将重新加载")
						FreeappCfg() //释放配置信息
						iniCfg()     //重新初始化配置信息
					}
				}
			case err, ok := <-watcher.Errors:
				if !ok {
					return
				}
				Log.Error("监听配置文件时发生异常:", err)
			}
		}
	}()

	// <-done // 阻塞,让调用watchConfigFile的线程进入阻塞状态
}

// 获取当前执行程序所在的绝对路径
func AppPath() string {
	exePath, err := os.Executable()
	if err != nil {
		return "./"
	}

	res, _ := filepath.EvalSymlinks(filepath.Dir(exePath))

	return res
}
